#include "testdcb.h"
#include <QtWidgets>
#include <QMetaEnum>
#include <cstdio>
#include <chrono>
#include <atomic>
#include <functional>

#include "CControlDlg.h"
#include "CQueryWidget.h"
#include "selfdefine.h"

namespace {
    constexpr unsigned char g_head{0xea};
    constexpr unsigned char g_real{0xeb};
    constexpr unsigned char g_sendsize{0x08};
    int g_exit{ 0 };
    int g_query_exit{ 0 };
}

using namespace std::chrono_literals;


#pragma execution_character_set("utf-8")
void testdcb::OnClose()
{
    auto dlg = (CControlDlg*)sender();
    Q_ASSERT(dlg);
    CControlDlg::ControlType type = dlg->GetControlType();
    unsigned char buf[8]{0};
    switch (type)
    {
    case CControlDlg::POWER_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 2;
        buf[3] = 0x11;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf,6);
        WriteData(buf);
    }
        break;
    case CControlDlg::ALARM_LIGHT_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 3;
        buf[3] = 0x12;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::RAY_LIGHT_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 4;
        buf[3] = 0x13;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::VOLTAGE_24_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 5;
        buf[3] = 0x14;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::SHUTDOWN_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 6;
        buf[3] = 0x15;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::KB_RAY_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 7;
        buf[3] = 0x16;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::KB_RUN_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 8;
        buf[3] = 0x17;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::CONVEYOR_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 9;
        buf[3] = 0x18;
        buf[5] = 1;//default forward
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::RAY_1_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 0xA;
        buf[3] = 0x19;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::RAY_2_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 0xB;
        buf[3] = 0x1A;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::VOLTAGE_12_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 0xC;
        buf[3] = 0x1B;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::UNKNOW_TYPE:
        break;
    default:
        break;
    }
 
}

void testdcb::OnQuery()
{
    if (m_serial)
    {
        unsigned char buf[]{g_head,g_sendsize,0x01,0x10,0x00,0x00,0x03,g_real};
        
        if (0 != m_serial->Write((char*)buf, sizeof(buf)))
            WriteMsg(buf, sizeof(buf));
    }
}

void testdcb::OnAutoSend(bool flag)
{
    if (flag)
    {
        g_query_exit = 0;
        m_query_thread = std::jthread(&testdcb::OnCicleQuery, this);
    }
    else
        g_query_exit = 1;
}

void testdcb::on_pushButton_clear_clicked()
{
    ui.plainTextEdit->clear();
}


void testdcb::OnOpen()
{
    auto dlg = (CControlDlg*)sender();
    Q_ASSERT(dlg);
    CControlDlg::ControlType type = dlg->GetControlType();
    unsigned char buf[8]{0};
    switch (type)
    {
    case CControlDlg::POWER_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 2;
        buf[3] = 0x11;
        buf[6] = CalculateChecksum((char*)buf,6);
        WriteData(buf);
    }
        break;
    case CControlDlg::ALARM_LIGHT_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 3;
        buf[3] = 0x12;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::RAY_LIGHT_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 4;
        buf[3] = 0x13;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::VOLTAGE_24_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 5;
        buf[3] = 0x14;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::SHUTDOWN_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 6;
        buf[3] = 0x15;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::KB_RAY_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 7;
        buf[3] = 0x16;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::KB_RUN_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 8;
        buf[3] = 0x17;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::CONVEYOR_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 9;
        buf[3] = 0x18;
        buf[4] = 1;
        buf[5] = 1;//default forward
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::RAY_1_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 0xA;
        buf[3] = 0x19;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::RAY_2_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 0xB;
        buf[3] = 0x1A;
        buf[4] = 1;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::VOLTAGE_12_TYPE:
    {
        ResetBuf((char*)buf);
        buf[2] = 0xC;
        buf[3] = 0x1B;
        buf[6] = CalculateChecksum((char*)buf, 6);
        WriteData(buf);
    }
        break;
    case CControlDlg::UNKNOW_TYPE:
        break;
    default:
        break;
    }
}

void OnReceive(BS::SerialPort* ptr, std::function<void(const QString& msg)>callback)
{
    if (ptr)
    {
        while (!g_exit)
        {
            unsigned char rbuf[256]{};
            auto len = ptr->Read((char*)rbuf, 256);
            if (0 < len)
            {
                QString result{"recv: "};
                for (int i = 0; i < len; ++i)
                {
                    result += QString("%1").arg(rbuf[i], 2, 16, QChar('0')).toUpper();
                    result += " ";
                }
                callback(result);
            }
            std::this_thread::sleep_for(30ms);
        }
    }
}

testdcb::testdcb(QWidget *parent)
    : QMainWindow(parent), m_serial{}
{
    ui.setupUi(this);
    OnInitDialog();
}

testdcb::~testdcb()
{
    g_exit = 1;
    UnInitSerial();
    if (m_thread.joinable())
        m_thread.join();
}

void testdcb::OnInitDialog()
{
    InitUI();
    InitSerial();
    connect(this, &testdcb::signal_receive_data, this, &testdcb::WriteReceiveMsg);
    auto callback = std::bind(&testdcb::signal_receive_data, this,std::placeholders::_1);
    m_thread =  std::thread(OnReceive,m_serial,callback);
}

void testdcb::InitSerial()
{
    if (nullptr == m_serial)
        m_serial = new BS::SerialPort();
    Q_ASSERT(m_serial);
    m_serial->SetTimeout(100);
    if (-1 == m_serial->Open(5, 38400))
        QMessageBox::warning(this, tr("warning"), tr("serial open fail code[%1]").arg(m_serial->GetErrorCode()));
}

void testdcb::UnInitSerial()
{
    if (m_serial)
        m_serial->Close();
}

void testdcb::InitUI()
{
    auto layout = new QGridLayout();
    layout->setHorizontalSpacing(6);
    layout->setVerticalSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    for (int i = CControlDlg::POWER_TYPE; i <= CControlDlg::VOLTAGE_12_TYPE; ++i)
    {
        auto widget = new CControlDlg(static_cast<CControlDlg::ControlType>(i));
        Q_ASSERT(widget);
        if (CControlDlg::SHUTDOWN_TYPE == i)
        {
            widget->SetOpenBtnName(tr("Shutdown"));
            widget->SetCloseBtnName(tr("Cancel"));
        }
        connect(widget, &CControlDlg::signal_open_clicked,this,&testdcb::OnOpen,Qt::UniqueConnection);
        connect(widget, &CControlDlg::signal_close_clicked, this, &testdcb::OnClose, Qt::UniqueConnection);
        widget->SetGroupTitle(QMetaEnum::fromType<CControlDlg::ControlType>().key(i));
        layout->addWidget(widget,i/6,i%6);
    }
    auto query = new CQueryWidget();
    connect(query, &CQueryWidget::signal_query_clicked, this, &testdcb::OnQuery, Qt::UniqueConnection);
    connect(query, &CQueryWidget::signal_checked, this, &testdcb::OnAutoSend, Qt::UniqueConnection);
    layout->addWidget(query, 1,5);
    
    ui.widget_btn->setLayout(layout);
}

void testdcb::WriteMsg(unsigned char* buf, int size)
{
    if (buf)
    {
        QString result{"send: "};
        for (int i = 0; i < size; ++i)
        {
            result += QString("%1").arg(buf[i],2,16,QChar('0')).toUpper();
            result += " ";
        }
        emit signal_receive_data(result);
    }
}

void testdcb::WriteMsg(const QString& msg)
{
    ui.plainTextEdit->appendPlainText(msg);
}

void testdcb::WriteData(unsigned char* buf, int size)
{
    if (buf)
        if (m_serial)
            if (0 < m_serial->Write((char*)buf, size))
                WriteMsg(buf, size);

}

void testdcb::OnCicleQuery()
{
    while (!g_query_exit)
    {
        if (m_serial)
        {
            unsigned char buf[]{ g_head,g_sendsize,0x01,0x10,0x00,0x00,0x03,g_real };

            if (0 != m_serial->Write((char*)buf, sizeof(buf)))
                WriteMsg(buf, sizeof(buf));
        }
        std::this_thread::sleep_for(50ms);
    }
}

void testdcb::WriteReceiveMsg(const QString& msg)
{
    ui.plainTextEdit->appendPlainText(msg);
}
